Skip to content

fix(cli): filter isMeta and isCompactSummary messages in local and remote mode#359

Merged
tiann merged 2 commits intotiann:mainfrom
hqhq1025:fix/filter-meta-and-compact-messages
Mar 24, 2026
Merged

fix(cli): filter isMeta and isCompactSummary messages in local and remote mode#359
tiann merged 2 commits intotiann:mainfrom
hqhq1025:fix/filter-meta-and-compact-messages

Conversation

@hqhq1025
Copy link
Contributor

Summary

When Claude Code loads a skill, it injects the full skill content as a user message with isMeta: true. This message bypasses existing filters in both local and remote mode, causing the full skill text to render as a regular chat message in the web UI.

Root cause

Verified by inspecting real JSONL session data — skill injection messages have this structure:

{ "type": "user", "isMeta": true, "message": { "content": [{ "type": "text", "text": "# Full skill content..." }] } }
  • Local mode (claudeLocalLauncher): only filtered summary and invisible system messages — isMeta user messages passed through
  • Remote mode (OutgoingMessageQueue): only filtered type === 'system' — skill injections have type: 'user' and slipped through

Also adds isCompactSummary filtering at the source for consistency with the web normalizer (normalizeAgent.ts:201-202), which already checks both fields.

Changes

File Change
claudeLocalLauncher.ts Add isMeta and isCompactSummary checks before sending
OutgoingMessageQueue.ts Add isMeta and isCompactSummary checks in send filter
claudeLocalLauncher.test.ts +2 tests (isMeta, isCompactSummary)
OutgoingMessageQueue.test.ts New file, 4 tests covering all filter conditions

Relates to #235

Test plan

  • 10 unit tests: local launcher (6) + outgoing queue (4), all passing
  • bun run typecheck passes
  • Load a skill in local mode, confirm skill content no longer appears as chat text
  • Load a skill in remote mode, confirm same
  • Confirm normal messages, tool calls, and visible system events (api_error) still display correctly

…mote mode

When Claude Code loads a skill, it injects the full skill content as a
user message with isMeta: true. In local mode, this message passes
through all existing filters (it's not a summary, not a system message)
and reaches the web UI where it renders as a regular chat message.

In remote mode, the OutgoingMessageQueue only filtered system-type
messages. Skill injections have type 'user' with isMeta: true and
slipped through.

Also filter isCompactSummary messages at the source for consistency
with the web normalizer, which already checks both fields.

Changes:
- claudeLocalLauncher: add isMeta and isCompactSummary checks
- OutgoingMessageQueue: add isMeta and isCompactSummary checks
- Add tests for both local launcher (6 tests) and queue (4 tests)

Relates to tiann#235
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • None.

Summary
Review mode: initial. No issues found in modified lines. Residual risk: runtime behavior depends on message flags; manual local/remote UI verification not run.

Testing

  • Not run (automation)

HAPI Bot

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • None.

Summary
Review mode: follow-up after new commits. No issues found in modified lines. Residual risk: runtime behavior depends on message flags; manual local/remote UI verification not run.

Testing

  • Not run (automation)

HAPI Bot

@tiann tiann merged commit 6384697 into tiann:main Mar 24, 2026
2 checks passed
Hwwwww-dev pushed a commit to Hwwwww-dev/hapi that referenced this pull request Mar 26, 2026
Hwwwww-dev added a commit to Hwwwww-dev/hapi that referenced this pull request Mar 26, 2026
Cherry-picked 15 upstream commits (post v0.16.3):
- fix(terminal): prevent infinite reconnect loop on Windows hosts (tiann#336)
- fix(cli): fix process exit handling deadlock in Claude SDK (tiann#343)
- fix(claude): handle async background task notifications in remote mode (tiann#354)
- fix(cli): filter invisible system messages in local mode (tiann#351)
- fix(cli): filter isMeta and isCompactSummary messages (tiann#359)
- fix(cli): prevent system-injected messages as user role (tiann#361)
- fix(hub): pass resumeSessionId when resuming session (tiann#337)
- fix(cli): treat any exit code as expected when abort requested (tiann#347)
- fix: Codex default approval policy for remote sessions (tiann#328)
- fix(hub): handle Telegram bot polling errors (tiann#350)
- fix(web): keep mobile views scrollable and new-session actions reachable (tiann#364)
- fix(web): hide unsupported Codex slash commands in remote mode (tiann#357)
- fix(web): restore mobile scrolling outside Telegram (tiann#358)
- fix(web): add visual indicator for sending message status (tiann#344)
- feat(claude): add effort setting parity with model across stack (tiann#353)

Additional fixes:
- Resolve type errors from upstream cherry-picks
- Add effort 'low' option and fix online WiFi icon
- DB schema V9 → V10 (effort column migration)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants